package com.lins.store.bo;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CopyObjectResult;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.lins.common.Params;
import com.lins.store.aop.checkParam.ParamNotNullCheck;
import com.lins.store.entity.FileEntity;
import com.lins.store.entity.TreeEntity;
import com.lins.store.mapper.FileMapper;
import com.lins.store.entity.ex.FileStateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Service
@Transactional
public class FileService {
    @Autowired
    Params params;

    @Autowired
    private FileMapper fileMapper;

    //地址数量上限(猜测是用户最大连接数) @Value()，spring在application.properties读取信息
    //@Value("${user.address.max-count}")
    //private int maxCount;




    @ParamNotNullCheck
    public List<FileEntity> findList(String uid,Map<String,String> paramMap) {
        paramMap.put("uid",uid);
        List<FileEntity> list = fileMapper.findList(paramMap);
        return list.size() == 0 ? nullData() : myImgUtil(list);
    }

    public List<FileEntity> search(String uid,Map<String, String> paramMap) {
        paramMap.put("uid",uid);
        List<FileEntity> data;
        //如果zi==01 代表不检索子文件夹 正常查询
        if ("01".equals(paramMap.get("zi"))){
            data = fileMapper.findList(paramMap);
        }else {
            //清空临时表
            fileMapper.emptyTemp();
            //如果zi==00 代表检索子文件夹 把所有属于该目录下的符合条件的文件拿到
            //根据url拿到文件夹id  根据文件夹id查子文件 如果子文件列表里有子文件夹  则拿到子文件夹id继续往下找文件  循环着找
            List<FileEntity> fileEntities = fileMapper.ziFile(paramMap.get("parUrl"));
            //第一次查出的当前文件夹下的所有文件    第一次没给赋值给返回值(查出来的结果，循环保存到临时表)
//            for (FileEntity f:fileEntities) fileMapper.saveTemp(f);
            fileEntities.forEach(fileMapper::saveTemp);
//            fileEntities.forEach(System.out::println);

            getDirectory(fileEntities);
            //从临时表查符合模糊查询条件的数据
            data = fileMapper.findTempByUid(paramMap);
        }
        return myImgUtil(data);
    }

    public List<TreeEntity> jsonTree(String uid) {
        List<TreeEntity> list = new ArrayList<>();
        fileMapper.queryFloderAll(Objects.requireNonNull(uid)).forEach(f -> list.add(new TreeEntity(f.getFid(),f.getParId(),f.getFileName())));
        return list;
    }

    public String fileUpload(String uid,MultipartFile file,Map<String, String> paramMap) {
        String filename = file.getOriginalFilename(),fileSize,id = "00";
        String parUrl = paramMap.get("parUrl");
        checkRepeat(uid,filename,parUrl,"文件名重复！");
        String path =parUrl +"/"+filename;
        OSS ossClient = new OSSClientBuilder().build(params.getEndpoint(),params.getAccessKeyId(), params.getAccessKeySecret());
        try {
            PutObjectRequest putObjectRequest = new PutObjectRequest(params.getBucketName(), path.substring(1), MultipartFileToFile(file));
            String contentType = file.getContentType();
            if ("text/plain".equals(contentType)){
                ObjectMetadata metadata = new ObjectMetadata();
                metadata.setContentType(contentType+";charset=utf-8");
                putObjectRequest.setMetadata(metadata);
            }
            ossClient.putObject(putObjectRequest);
        } catch (OSSException oe) {
            log.error("捕获了一个OSSException异常，这意味着你的请求被发送到了OSS,但由于某种原因被拒绝了，并给出了错误响应。");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
            throw new FileStateException("OSS异常！");
        } catch (ClientException ce) {
            log.error("捕获了一个ClientException，这意味着客户端在试图与OSS通信时遇到了严重的内部问题，例如无法访问网络。");
            log.error("Error Message:" + ce.getMessage());
            throw new FileStateException("客户端异常！");
        } finally {
            if (ossClient != null) ossClient.shutdown();
        }
        DecimalFormat df = new DecimalFormat("0.00");
        if (file.getSize() < 1024) {
            fileSize = df.format((double) file.getSize()) + "字节";
        } else if (file.getSize() < 1048576) {
            fileSize = df.format((double) file.getSize() / 1024) + "KB";
        } else if (file.getSize() < 1073741824) {
            fileSize = df.format((double) file.getSize() / 1048576) + "MB";
        } else {
            fileSize = df.format((double) file.getSize() / 1073741824) + "GB";
        }

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        FileEntity fileEntity = new FileEntity(filename,fileSize,simpleDateFormat.format(new Date()),"01",uid.toString(),path,"00",paramMap.get("parUrl") == null ? "/home" : paramMap.get("parUrl"));
        if (fileMapper.getParIdByParUrl(fileEntity.getParUrl()) != null) id = fileMapper.getParIdByParUrl(fileEntity.getParUrl()).getFid();
        fileEntity.setParId(id);
        fileMapper.saveFile(fileEntity);
        return path;
    }

    public void downloadFile(String uid,String fid, HttpServletResponse response) {
        InputStream is = null;
        OutputStream os = null;
        OSS ossClient = new OSSClientBuilder().build(params.getEndpoint(), params.getAccessKeyId(), params.getAccessKeySecret());
        try {
            assert uid != null;
            FileEntity fileEntity = fileMapper.findByFidAndUid(fid, uid);
            if (fileEntity == null)  throw new FileStateException("目标文件为空!或别人未开启共享权限！");
            String fileName = fileEntity.getFileName();
            String ossUrl = fileEntity.getUrl().substring(1);
            OSSObject ossObject = ossClient.getObject(params.getBucketName(), ossUrl);
            is = ossObject.getObjectContent();
            response.reset();
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=" + new String((fileName).getBytes("GBK"), "ISO8859-1"));
            os = new BufferedOutputStream(response.getOutputStream());
            byte[] bytes = new byte[10240];
            int len;
            while ((len = is.read(bytes)) != -1) os.write(bytes, 0, len);
            ossObject.close();
        } catch (OSSException oe) {
            log.error("捕获了一个OSSException异常，这意味着你的请求被发送到了OSS");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());

            /*log.error(LinsUtil.print("捕获了一个OSSException异常，这意味着你的请求被发送到了OSS。" +
                    "Error Message:{}，Error Code:{}，Request ID：{}，Host ID：{}",
                    oe.getErrorMessage(),oe.getErrorCode(),oe.getRequestId(),oe.getHostId()));
            throw new FileStateException("OSS-Server Exception");*/
        } catch (Throwable ce) {
            log.error("捕获了一个ClientException，这意味着客户端在试图与OSS通信时遇到了严重的内部问题，例如无法访问网络");
            log.error("Error Message:" + ce.getMessage());
            throw new FileStateException("ClientException");
        } finally {
            try {
                if(os !=null){
                    os.flush();
                    os.close();
                }
                if(is != null){
                    is.close();
                }
            } catch (IOException e) {}
            if (ossClient != null) ossClient.shutdown();
        }
    }

    public Integer delFileByFid(String uid,String fid) {
        Integer integer = fileMapper.delFileByFid(fid);
        //如果文件的parid，在文件的fid不存在则删除
        fileMapper.delNoParFile();
        return integer;
    }

    public Integer saveFloder(String uid,FileEntity fileEntity) {
        String fileName = fileEntity.getFileName();
//        if (-1 != fileName.indexOf(".")){
        if (fileName.contains(".")){
            //throw new FileStateException("文件夹名称不能含有<.>特殊符号！"); //也可自动替换成<_>
            fileName = fileName.replace(".","_");
        }
        //检验同级目录下 文件名是否重复
        checkRepeat(uid,fileName,fileEntity.getParUrl(),"文件夹名称重复！");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        fileEntity.setFileSize("……");
        fileEntity.setCreateTime(simpleDateFormat.format(new Date()));
        fileEntity.setShare("01");
        fileEntity.setUid(uid.toString());
        fileEntity.setUrl(fileEntity.getParUrl()+"/" + fileEntity.getFileName());
        //fileEntity.setParUrl("/home"); //前端赋值
        fileEntity.setFileType("01");
        fileEntity.setFileName(fileName);
        String parId = "00";
        if (fileMapper.getParIdByParUrl(fileEntity.getParUrl()) != null){
            parId = fileMapper.getParIdByParUrl(fileEntity.getParUrl()).getFid();
        }
        fileEntity.setParId(parId);
        return fileMapper.saveFile(fileEntity);
    }

    public Integer delFileByUid(String uid) {
        return fileMapper.delFileByUid(uid);
    }

    public Integer moveFile(String uid,Map<String,String> paramMap) {
        return  fileMapper.moveFile(paramMap);
    }

    public Integer moveFileRun(String uid,ArrayList<String> list) {
        Map<String,String> paramMap = new HashMap<>();
        String fid = list.get(1);
        String nodeid = list.get(3);
        paramMap.put("fid",fid);
        paramMap.put("nodeid",nodeid);
        FileEntity fileEntity = fileMapper.findByFid(fid);
        String fileName = fileEntity.getFileName();
        //pid=nodeid的目录 是否存在该文件名
        checkRepeat("",nodeid, uid,fileName,"文件名重复！");
        String sourceKey = fileEntity.getUrl().substring(1);
        String destinationKey;
        if ("00".equals(nodeid)){
            destinationKey = "home/"+fileEntity.getFileName();
        }else {
            FileEntity nodeEntity = fileMapper.findByFid(nodeid);
            destinationKey = nodeEntity.getUrl().substring(1)+"/"+fileEntity.getFileName();
        }
        OSS ossClient = new OSSClientBuilder().build(params.getEndpoint(), params.getAccessKeyId(), params.getAccessKeySecret());
        try {
            CopyObjectResult result = ossClient.copyObject(params.getBucketName(), sourceKey, params.getBucketName(), destinationKey);
            log.info("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
        } catch (OSSException oe) {
            log.error("OSS异常");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
            throw new FileStateException("OSSException！");
        } catch (ClientException ce) {
            log.error("Error Message:" + ce.getMessage());
            throw new FileStateException("ClientException！");
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return fileMapper.moveFileRun(paramMap);
    }

    public Integer changeShare(String uid,Map<String, String> paramMap) {
        return fileMapper.changeShare(paramMap);
    }

    public Integer renameByUid(String uid,FileEntity fileEntity) {
        FileEntity byFid = fileMapper.findByFid(fileEntity.getFid());
        String newFileName = fileEntity.getFileName();
        String oldFileName = byFid.getFileName();
        String parUrl = byFid.getParUrl();
        int index = oldFileName.lastIndexOf(".");
        FileEntity paramEntity = new FileEntity();
        //文件 改变新文件名
        if (index >= 0) newFileName = newFileName+oldFileName.substring(index);
        checkRepeat(uid,newFileName,parUrl,"目标文件名重复！");
        String oldUrl = parUrl.substring(1)+"/"+ oldFileName;
        String newUrl = parUrl+"/"+ newFileName;
        paramEntity.setFid(fileEntity.getFid());
        paramEntity.setFileName(newFileName);
        paramEntity.setUrl(newUrl);
        OSS ossClient = new OSSClientBuilder().build(params.getEndpoint(), params.getAccessKeyId(), params.getAccessKeySecret());
        try {
            CopyObjectResult result = ossClient.copyObject(params.getBucketName(), oldUrl, params.getBucketName(), newUrl.substring(1));
            log.info("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
        } catch (OSSException oe) {
            log.error("OSS异常");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
            throw new FileStateException("OSS异常！");
        } catch (ClientException ce) {
            log.error("Error Message:" + ce.getMessage());
            throw new FileStateException("客户端异常！");
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return fileMapper.renameByUid(paramEntity);
    }

    //---------------------------------------------
    public List<FileEntity> queryFloderAll(String uid) {
        return fileMapper.queryFloderAll(uid);
    }

    public Integer saveFile(FileEntity fileEntity) {
        Integer integer = fileMapper.saveFile(fileEntity);
        return integer;
    }

    public FileEntity findByFid(String fid) {
        return fileMapper.findByFid(fid);
    }

    public List<FileEntity> queryByPid(String pid) {
        return fileMapper.queryByPid(pid);
    }

    public List<FileEntity> findByUidAndFilename(FileEntity fileEntity) {
        return fileMapper.findByUidAndFilename(fileEntity);
    }

    public FileEntity findByPidAndFilename(Map<String, String> paramMap) {
        return fileMapper.findByPidAndFilename(paramMap);
    }

    public FileEntity findByFidAndUid(String fid,String uid) {
        return fileMapper.findByFidAndUid(fid, uid);
    }

    public FileEntity getParIdByParUrl(String parUrl) {
        return fileMapper.getParIdByParUrl(parUrl);
    }

    public List<FileEntity> ziFile(String url) {
        return fileMapper.ziFile(url);
    }

    public List<FileEntity> ziFileByFid(String fid) {
        return fileMapper.ziFileByFid(fid);
    }

    public Integer saveTemp(FileEntity fileEntity) {
        return fileMapper.saveTemp(fileEntity);
    }

    public Integer emptyTemp() {
        return fileMapper.emptyTemp();
    }

    public List<FileEntity> findTempByUid(Map<String, String> paramMap) {
        return fileMapper.findTempByUid(paramMap);
    }

    //--------------------------------------------

    /**
     * 处理空列表时的假数据
     * @return
     */
    public List<FileEntity> nullData(){
        String p = "……";
        FileEntity nullEntity = new FileEntity(p,p,p,p,p,p,p,p,"..png");
        ArrayList<FileEntity> nullData = new ArrayList<>();
        nullData.add(nullEntity);
//        return nullData;
        return null;
    }

    /**
     * 处理文件图标
     * @param data
     * @return
     */
    private List<FileEntity> myImgUtil(List<FileEntity> data){
        for (FileEntity f:data) {
            int index = f.getFileName().lastIndexOf(".");
            //文件夹
            if (index < 0){
                f.setImgUrl("floder.png");
            }else {
                f.setImgUrl(f.getFileName().substring(index + 1)+".png");
            }
        }
        return data;
    }

    /**
     * 递归查找
     * @param fileEntities
     */
    private void getDirectory(List<FileEntity> fileEntities){
        if (fileEntities == null || fileEntities.size() == 0) return;
        //循环判断所有文件是否属于文件夹
        for (FileEntity f:fileEntities) {
            //属于文件夹则继续根据文件夹id循环查找
            if ("01".equals(f.getFileType())){
                List<FileEntity> fileEntitiesEle = fileMapper.ziFileByFid(f.getFid());
                //查出来的结果，循环保存到临时表
//                for (FileEntity t:fileEntitiesEle) fileMapper.saveTemp(t);
                fileEntitiesEle.forEach(fileMapper::saveTemp);
                getDirectory(fileEntitiesEle);
            }
        }
    }

    /**
     * 上传的文件存放本地
     */
    /*@PostMapping("fileUpload")
    public JsonResult<String> changeAvatar(@RequestParam("file") MultipartFile file,@RequestParam Map<String, String> paramMap, HttpSession session) {
        Integer uid = getUidFromSession(session);
        String filename = file.getOriginalFilename();
        //通过uid查询本人文件是否重名
        if ( fileService.findByUidAndFilename(uid, filename).size() > 0){
            System.err.println("文件名重复！");
            throw new FileStateException("文件名重复!");
        }
        //String parent = session.getServletContext().getRealPath("upload");
        String parent = "D:\\MyCode\\store\\src\\main\\resources\\static\\web\\upload";
        File dir = new File(parent);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File dest = new File(dir, filename);
        try {
            file.transferTo(dest);
        } catch (IllegalStateException e) {
            throw new FileStateException("文件状态异常，可能文件已被移动或删除");
        } catch (IOException e) {
            throw new FileUploadIOException("上传文件时读写错误，请稍后再次尝试");
        }
        String path = paramMap.get("parUrl")+"/"+filename;
        String fileSize = "";
        DecimalFormat df = new DecimalFormat("0.00");
        if (file.getSize() < 1024) {
            fileSize = df.format((double) file.getSize()) + "字节";
        } else if (file.getSize() < 1048576) {
            fileSize = df.format((double) file.getSize() / 1024) + "KB";
        } else if (file.getSize() < 1073741824) {
            fileSize = df.format((double) file.getSize() / 1048576) + "MB";
        } else {
            fileSize = df.format((double) file.getSize() / 1073741824) + "GB";
        }
        FileEntity fileEntity = new FileEntity();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        fileEntity.setFileName(filename);
        fileEntity.setFileSize(fileSize);
        fileEntity.setCreateTime(simpleDateFormat.format(new Date()));
        fileEntity.setShare("01");
        fileEntity.setUid(uid.toString());
        fileEntity.setUrl(path);
        fileEntity.setParUrl(paramMap.get("parUrl") == null ? "/home" : paramMap.get("parUrl"));
        fileEntity.setFileType("00");
        String id = "00";
        if (fileService.getParIdByParUrl(fileEntity.getParUrl()) != null){
            id = fileService.getParIdByParUrl(fileEntity.getParUrl()).getFid();
        }
        fileEntity.setParId(id);
        fileService.saveFile(fileEntity);
        return new JsonResult<String>(OK, path);
    }*/

    /**
     * 将MultipartFile转换为File
     * @param multiFile
     * @return
     */
    public static File MultipartFileToFile(MultipartFile multiFile) {
        // 获取文件名
        String fileName = multiFile.getOriginalFilename();
        // 获取文件后缀
        assert fileName != null;
        String prefix = fileName.substring(fileName.lastIndexOf("."));
        // 若须要防止生成的临时文件重复,能够在文件名后添加随机码
        try {
            File file = File.createTempFile(fileName, prefix);
            multiFile.transferTo(file);
            return file;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 检验同级目录下 文件名是否重复
     * @param uid
     * @param filename
     * @param parUrl
     * 也不一定非要写两个重载方法，也可以根据第三个参数，扫描string是否携带“/”来区分是parUrl还是pid
     * 从而区分两种不同情况下的逻辑
     */
    public void checkRepeat(String uid,String filename,String parUrl,String message){
        if (fileMapper.findByUidAndFilename(new FileEntity(filename,uid,parUrl)).size() > 0)
            throw new FileStateException(message);
    }

    public void checkRepeat(String qf,String pid,String uid,String filename,String message){
        //根据pid作为fid拿来url作为parUrl
        FileEntity byFid = fileMapper.findByFid(pid);
        String parUrl = byFid == null ? "/home" : byFid.getUrl();
        if (fileMapper.findByUidAndFilename(new FileEntity(filename,uid,parUrl)).size() > 0)
            throw new FileStateException(message);
    }

    //同級目錄下文件名重複則自動修改文件名
    public void checkRepeatAndModifi(String uid,String filename,String parUrl,String message){
        /**
         * 先查詢，若同級目錄存在相同的文件名，則修改文件名為(1)后繼續入庫
         * 若修改后的名字仍然重複，則取出原文件名，遞增修改括號內數字後繼續入庫
         * 所以在实现時：每次修改文件名之前需要先截取文件名判斷是否攜帶括號，也就是曾經是否被修改過？
         * 更嚴謹的做法是維護一個字段，用來保存該文件名被系統強制修改后的次數
         *
         * 注意：ctrl+shift+f  切换简体字与繁體字
         **/


    }

    /*@RequestMapping("download/{fid}")
    public void downloadFile(@PathVariable("fid")String fid, HttpServletResponse response,HttpSession session) {
        InputStream is = null;
        OutputStream os = null;
        try {
            Integer uid = getUidFromSession(session);
            FileEntity fileEntity = fileService.findByFidAndUid(fid, uid.toString());
            String url = fileEntity.getUrl();
            String fileName = fileEntity.getFileName();
            String filePath = "D:\\MyCode\\store\\src\\main\\resources\\static\\web\\upload\\"+fileName;
            response.reset();
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=" + new String((fileName).getBytes("GBK"), "ISO8859-1"));
            is = new FileInputStream(new File(filePath));
            os = new BufferedOutputStream(response.getOutputStream());
            byte[] bytes = new byte[10240];
            int len;
            while ((len = is.read(bytes)) != -1) {
                os.write(bytes, 0, len);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if(os !=null){
                    os.flush();
                    os.close();
                }
                if(is != null){
                    is.close();
                }
            } catch (IOException e) {
            }
        }
    }*/



}
